<?php

namespace App\Kuafu\Pay\Channel\FuYou;

use App\Kuafu\Pay\Enum\BasePayEnum;
use App\Kuafu\Pay\Enum\FuYouPayEnum;
use App\Kuafu\Pay\Model\PayStreamModel;
use App\Library\SnowFlake\SnowFlake;
use GuzzleHttp\Client;
use GuzzleHttp\RequestOptions;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;

/**
 * 富友支付的Mock类，单测/压测用
 */
class FuYouPayMock extends FuYouPay
{
    const CACHE_TAG    = 'FUYOU_PAY_MOCK_TAG';
    const FACE_PAY_KEY = 'FACE_PAY_KEY:';
    const REFUND_KEY   = 'REFUND_KEY:';

    private int $tradeStatus = BasePayEnum::TRADE_STATUS_OK;

    /**
     * 手动设置交易结果
     * @param int $tradeStatus
     */
    public function setTradeStatus(int $tradeStatus): self
    {
        $this->tradeStatus = $tradeStatus;
        return $this;
    }

    public function faceToFacePay(string $subject, int $amount, string $authCode = ''): array
    {
        $randNum = rand(0, 1);
        if ($randNum) {
            $payChannelChild = PayStreamModel::PAY_CHANNEL_ALI;
        } else {
            $payChannelChild = PayStreamModel::PAY_CHANNEL_WX;
        }

        // 自动加载payChannelChild
        $this->fyObj->loadChannel($payChannelChild);

        $payNo = $this->getOutTradeNo();

        $successTime = 0;
        $thirdPayNo  = '';

        $this->mockSleep();

        switch ($this->tradeStatus) {
            case BasePayEnum::TRADE_STATUS_OK:
                $rStatus = true;

                // 付款成功
                $tradeStatus = BasePayEnum::TRADE_STATUS_OK;

                // 付款成功时间
                $successTime = time();

                // 三方流水号
                $thirdPayNo = SnowFlake::nextIdCache();
                break;

            // 失败
            case BasePayEnum::TRADE_STATUS_CLOSED:
                $rStatus = false;

                $tradeStatus = BasePayEnum::TRADE_STATUS_CLOSED;
                break;

            default:
                $rStatus = false;

                // 待用户输入密码
                $tradeStatus = BasePayEnum::TRADE_STATUS_WAIT;
                break;
        }

        $data = [
            'payNo'             => $payNo,
            'thirdPayNo'        => $thirdPayNo,
            'tradeStatus'       => $tradeStatus,
            'commonTradeStatus' => $tradeStatus,
            'paySuccessTime'    => $successTime,
            'totalPrice'        => $amount,
        ];

        $redisKey = sprintf(self::FACE_PAY_KEY . '%s', $payNo);
        Cache::store('redis')
             ->tags(self::CACHE_TAG)
             ->put($redisKey, $data, 1200);

        return $this->returnData($rStatus, $data);
    }

    public function closePay(string $outTradeNo): array
    {
        $this->mockSleep();

        $data['outTradeNo'] = $outTradeNo;

        return $this->returnData(true, $data);
    }

    public function refund(string $outTradeNo, int $refundAmount): array
    {
        $this->mockSleep();

        $data = [
            'fundChange'        => true, // 本次退款是否发生了资金变化
            'payNo'             => $outTradeNo, // 原支付流水号
            'refundNo'          => $refundNo = $this->getOutTradeNo(), // 退款唯一流水号
            'thirdRefundNo'     => SnowFlake::nextIdCache(),
            'refundAmount'      => $refundAmount,
            'totalAmount'       => $refundAmount,
            'refundStatus'      => BasePayEnum::REFUND_STATUS_OK,
            'refundSuccessTime' => time(),

        ];

        $redisKey = sprintf(self::REFUND_KEY . '%s', $refundNo);
        Cache::store('redis')
             ->tags(self::CACHE_TAG)
             ->put($redisKey, $data, 1200);

        return $this->returnData(true, $data);
    }

    public function query(string $outTradeNo)
    {
        $this->mockSleep(1);

        $redisKey = sprintf(self::FACE_PAY_KEY . '%s', $outTradeNo);

        $data = Cache::store('redis')
                    ->tags(self::CACHE_TAG)
                    ->get($redisKey);

        return $this->returnData(true, $data);
    }

    public function refundQuery(string $outTradeNo, string $outRequestNo)
    {
        $this->mockSleep(1);

        $redisKey = sprintf(self::FACE_PAY_KEY . '%s', $outRequestNo);

        $data = Cache::store('redis')
                    ->tags(self::CACHE_TAG)
                    ->get($redisKey);

        return $this->returnData(true, (array)$data);
    }

    public function miniPay(string $orderDesc, int $amount, string $openId)
    {
        $this->mockSleep();

        $payNo = $this->getOutTradeNo();

        $data = [
            'wx'    => [
                'appId'     => $formatRes['oriData']['sdk_appid'] ?? '',
                'timeStamp' => $formatRes['oriData']['sdk_timestamp'] ?? '',
                'nonceStr'  => $formatRes['oriData']['sdk_noncestr'] ?? '',
                'package'   => $formatRes['oriData']['sdk_package'] ?? '',
                'paySign'   => $formatRes['oriData']['sdk_paysign'] ?? '',
                'signType'  => $formatRes['oriData']['sdk_signtype'] ?? '',
            ],
            'payNo' => $payNo,
        ];

        $redisKey = sprintf(self::FACE_PAY_KEY . '%s', $payNo);
        Cache::store('redis')
             ->tags(self::CACHE_TAG)
             ->put($redisKey,  [
                 'payNo'             => $payNo,
                 'thirdPayNo'        => SnowFlake::nextIdCache(),
                 'tradeStatus'       => FuYouPayEnum::TRADE_STATUS_OK,
                 'commonTradeStatus' => BasePayEnum::TRADE_STATUS_OK,
                 'paySuccessTime'    => time(),
                 'totalPrice'        => $amount,
             ], 1200);

        $response = (new Client([
            RequestOptions::TIMEOUT => 3,
        ]))->post('https://uat-api.51kuafu.com/api/pay/callback/fy/pay', $requestParam = ['json' => [
            'mchnt_order_no' => $payNo,
        ]]);

        Log::info("myfycallback response...", json_decode($response->getBody()->getContents(), true) ?? []);

        return $this->returnData(true, $data);
    }

    private function mockSleep(int $fixedSeconds = 0)
    {
        if ($fixedSeconds) {
            sleep($fixedSeconds);
            return;
        }

        $r = rand(1, 50);
        if ($r == 50) {
            sleep(8);
        }

        sleep(rand(1, 3));
    }

    private function returnData(bool $status, array $data)
    {
        return ['status' => $status, 'data' => $data];
    }

    public static function clearTag()
    {
        return Cache::store('redis')->tags(self::CACHE_TAG)->flush();
    }
}
